package panda.natalia.gothread.suspend.instrument;


import org.objectweb.asm.*;
import org.objectweb.asm.tree.AbstractInsnNode;

import static panda.natalia.gothread.suspend.GlobalDefine.isYieldMethod;
import static panda.natalia.gothread.suspend.instrument.GoThreadMethodInstrumentor.ASMAPI;

/**
 * a simple framework of java fiber
 *
 * @Author: scpanda2007
 * @Date: 2019/12/9 17:04
 */
public class LabelSuspendableCallSitesClassVisitor extends ClassVisitor {

    private final MethodDatabase db;
    private String className;

    public LabelSuspendableCallSitesClassVisitor(ClassVisitor classVisitor, MethodDatabase db) {
        super(ASMAPI, classVisitor);
        this.db = db;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces){
        this.className = name;
        if(version < Opcodes.V1_5) version = Opcodes.V1_5;

        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public MethodVisitor visitMethod(final int acess, final String name, String desc, final String signature, final String[] exceptions){
        if((acess & Opcodes.ACC_NATIVE) == 0 && !isYieldMethod(className, name)) {
            final MethodVisitor outMV = super.visitMethod(acess, name, desc, signature, exceptions);

            return new MethodVisitor(ASMAPI, outMV) {

                private int currLineNumber = -1;

                @Override
                public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
                    final int type = AbstractInsnNode.METHOD_INSN;
                    if(InstrumentMethod.isSuspendableCall(db, type, opcode, owner, name, desc)){
                        final Label l = new Label();
                        super.visitLabel(l);
                        super.visitLineNumber(currLineNumber, l);
                    }
                    super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
                }

                @Override
                public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
                    final int type = AbstractInsnNode.INVOKE_DYNAMIC_INSN;
                    final int opcode = Opcodes.INVOKEDYNAMIC;
                    if(InstrumentMethod.isSuspendableCall(db, type, opcode, bootstrapMethodHandle.getOwner(), name, desc)){
                        final Label l = new Label();
                        super.visitLabel(l);
                        super.visitLineNumber(currLineNumber, l);
                    }
                    super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
                }

                @Override
                public void visitLineNumber(int line, Label start) {
                    currLineNumber = line;
                    super.visitLineNumber(line, start);
                }
            };
        }
        return super.visitMethod(acess, name, desc, signature, exceptions);
    }

}
